<?php

/**
 * Matcher.php
 *
 * This class encapsulates regular expression in an object oriented fasion.
 *
 * @copyright	Jonah Dahlquist <jonah[at]nucleussystems[dot]com>
 * @author		Jonah Dahlquist <jonah[at]nucleussystems[dot]com>
 */

class Matcher
{
	const DELIMITER_DEFAULT = '/';
	const KEYWORD_DEFAULT = '{?}';
	
	protected $regex;

	/**
	 * Constructor.
	 *
	 * Create a new Matcher object
	 *
	 * @param string $regex
	 */
	public function __construct($regex, $escapeValues = array(), $delimiter = DELIMITER_DEFAULT, $keyword = KEYWORD_DEFAULT)
	{
		$this->setRegex($regex, $escapedValues, $delimiter, $keyword);
	}

	public function getRegex()
	{
		return $this->regex;
	}

	public function setRegex($regex, $escapeValues = array(), $delimiter = DELIMITER_DEFAULT, $keyword = KEYWORD_DEFAULT)
	{
		$index = 0;
		$length = strlen($keyword);
		foreach ($escapeValues as $escapeValue) {
			$pos = strpos($regex, $keyword, $index);
			if ($pos === false) {
				break;
			}
			$regex = substr_replace($regex, preg_quote($escapeValue, $delimiter), $pos, $length);
			$pos = $pos + $length;
		}
		$this->regex = $regex;
	}

	/**
	 * Check if a string matches this regular expression.
	 *
	 * @param string $string String to check
	 * @return boolean True if matches, false otherwise
	 */
	public function matches($string)
	{
		return preg_match($this->regex, $string) != 0;
	}

	/**
	 * Match regular expression against a string, and get the resulting match.
	 *
	 * @param string $string String to check
	 * @param boolean $captureData Set to true to get offset and match length
	 * @return array All matches
	 */
	public function match($string)
	{
		preg_match($this->regex, $string, $subpatterns);
		return $subpatterns;
	}

	public function matchOffset($string)
	{
		preg_match($this->regex, $string, $subpatterns, PREG_OFFSET_CAPTURE);
		foreach ($subpatterns as &$subpattern) {
			$subpattern = (object) array(
				'match' => $subpattern[0],
				'offset' => $subpattern[1],
				'length' => strlen($subpattern[0])
			);
		}
		return $subpatterns;
	}

	/**
	 * Match this regex against a string, and get all resulting matches.
	 *
	 * @param string $string String to check
	 * @param boolean $captureData Set to true to get offset and match length
	 * @return array All matches
	 */
	public function matchAll($string)
	{
		preg_match_all($this->regex, $string, $matches, PREG_SET_ORDER);
		return $matches;
	}

	public function matchAllOffsets($string)
	{
		preg_match_all($this->regex, $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
		foreach ($matches as &$subpatterns) {
			foreach ($subpatterns as &$subpattern) {
				$subpattern = (object) array(
					'match' => $subpattern[0],
					'offset' => $subpattern[1],
					'length' => strlen($subpattern[0])
				);
			}
		}
		return $matches;
	}

	public function replace($string, $replacement, $limit = -1, &$count = null)
	{
		return preg_replace($this->regex, $replacement, $string, $limit, $count);
	}

	public function replaceCallback($string, $callback, $replacement, $limit = -1, &$count = null)
	{
		return preg_replace_callback($this->regex, $callback, $replacement, $string, $limit, $count);
	}

	public function split($string, $limit = 1, $flags = 0)
	{
		return preg_split($this->regex, $string, $limit, $limit, $flags);
	}

	public function splitOffset($string, $limit = 1, $flags = 0)
	{
		$array = preg_split($this->regex, $string, $limit, $flags);
		foreach ($array as &$indice) {
			$indice = (object) array(
				'match' => $indice[0],
				'offset' => $indice[1],
				'length' => strlen($indice[0])
			);
		}
		return $array;
	}

	public function grep($input, $invert = false)
	{
		return preg_grep($this->regex, $input, $invert ? PREG_GREP_INVERT : 0);
	}

	public function getError()
	{
		return preg_last_error();
	}
}